import com.sun.deploy.net.proxy.RemoveCommentReader;

/**
 * @Author: linzp
 * @Date: 2022/07/05/16:17
 * 双重校验锁-单例模式
 */
public class Singleton {

    /**
     * 为什么是双重校验锁实现单例模式呢？
     *
     * 第一次校验：也就是第一个if（singleton==null），这个是为了代码提高代码执行效率，由于单例模式只要一次创建实例即可，所以当创建了一个实例之后，再次调用getInstance方法就不必要进入同步代码块，不用竞争锁。直接返回前面创建的实例即可。
     *
     * 第二次校验：也就是第二个if（singleton==null），这个校验是防止二次创建实例，假如有一种情况，当singleton还未被创建时，线程t1调用getInstance方法，由于第一次判断singleton==null，此时线程t1准备继续执行，但是由于资源被线程t2抢占了，此时t2页调用getInstance方法，同样的，由于singleton并没有实例化，t2同样可以通过第一个if，然后继续往下执行，同步代码块，第二个if也通过，然后t2线程创建了一个实例singleton。此时t2线程完成任务，资源又回到t1线程，t1此时也进入同步代码块，如果没有这个第二个if，那么，t1就也会创建一个singleton实例，那么，就会出现创建多个实例的情况，但是加上第二个if，就可以完全避免这个多线程导致多次创建实例的问题。
     *
     * 所以说：两次校验都必不可少。
     * 还有，这里的private static volatile Singleton singleton = null;中的volatile也必不可少，volatile关键字可以防止jvm指令重排优化，
     *
     * 因为 singleton = new Singleton() 这句话可以分为三步：
     *      1. 为 singleton 分配内存空间；
     *      2. 初始化 singleton；
     *      3. 将 singleton 指向分配的内存空间。
     *      但是由于JVM具有指令重排的特性，执行顺序有可能变成 1-3-2。 指令重排在单线程下不会出现问题，但是在多线程下会导致一个线程获得一个未初始化的实例。例如：线程T1执行了1和3，此时T2调用 getInstance() 后发现 singleton 不为空，因此返回 singleton， 但是此时的 singleton 还没有被初始化。
     *      使用 volatile 会禁止JVM指令重排，从而保证在多线程下也能正常执行。
     *
     * 这里还说一下volatile关键字的第二个作用，保证变量在多线程运行时的可见性：
     *
     * 在 JDK1.2 之前，Java的内存模型实现总是从主存（即共享内存）读取变量，是不需要进行特别的注意的。而在当前 的 Java 内存模型下，线程可以把变量保存本地内存（比如机器的寄存器）中，而不是直接在主存中进行读写。这就 可能造成一个线程在主存中修改了一个变量的值，而另外一个线程还继续使用它在寄存器中的变量值的拷贝，造成数 据的不一致。 要解决这个问题，就需要把变量声明为 volatile，这就指示 JVM，这个变量是不稳定的，每次使用它都到主存中进行 读取。
     */
    private Singleton() {
    }

    private static volatile Singleton singleton = null;

    public static Singleton getSingleton() {
        //第一次校验singleton是否为空
        if (singleton == null) {
            synchronized (Singleton.class) {
                //第一次校验singleton是否为空
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }

        }
        return singleton;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(new Runnable() {
                public void run() {
                    System.out.println(Thread.currentThread().getName() + " : " + Singleton.getSingleton().hashCode());
                }
            }).start();
        }
    }

    class Solution {
        public int removeDuplicates(int[] nums) {
            int slow = 0;
            for (int fast : nums) {
                if (nums[slow] != nums[fast]) {
                    nums[slow++] = nums[fast];
                }
            }
            return slow;


        }
    }

    /**
     * 977. 有序数组的平方
     * 给你一个按 非递减顺序 排序的整数数组 nums，返回 每个数字的平方 组成的新数组，要求也按 非递减顺序 排序。
     * <p>
     * <p>
     * <p>
     * 示例 1：
     * <p>
     * 输入：nums = [-4,-1,0,3,10]
     * 输出：[0,1,9,16,100]
     * 解释：平方后，数组变为 [16,1,0,9,100]
     * 排序后，数组变为 [0,1,9,16,100]
     * 示例 2：
     * <p>
     * 输入：nums = [-7,-3,2,3,11]
     * 输出：[4,9,9,49,121]
     */
    static class Solution2 {
        public static void main(String[] args) {
            int i = 0;
            int[] nums = {0, 0, 1, 1, 1, 2, 2, 3, 3, 4};
            for (int a = 0; a < nums.length; a++) {
                nums[i++] = 0;
            }

        }


        public int[] sortedSquares(int[] nums) {
            //现象成一个V字型数组 最大值在两边，最小值在中间
            int right = nums.length - 1, left = 0;
            int[] result = new int[nums.length];
            int index = nums.length - 1;
            while (left <= right) {
                if (nums[left] * nums[left] <= nums[right] * nums[right]) {
                    result[index--] = nums[right] * nums[right];
                    right--;
                } else {
                    result[index--] = nums[left] * nums[left];
                    left++;
                }
            }
            return result;

        }

    }


}
